﻿#pragma once

#include "common.h"
#include "ui/ui.h"
#include "bind.h"
#include "keyed.h"
#include <map>

namespace anip
{
	namespace ui
	{
		class Animation
		{
			friend class Control;

		private:
			struct Entry
			{
				_bind_notifiable* var;
				Control* parent;
				float endTime;
			};

			static std::map<_bind_notifiable*, Entry> _entries;

			static void _update();
			static bool _isActive() { return !_entries.empty(); }
			static void _removeControl(Control& c);

		public:
			template<typename T>
			static void curve(bindable<T>& var, Control& ctrl, T valTo, float duration, kfloat curve)
			{
				if (duration <= 0)
				{
					var = valTo;
					return;
				}

				T from = var();
				float startTime = (float)anip::time();
				var = [=]()
				{
					float dt = (float)anip::time() - startTime;
					if (dt > duration) return valTo;
					T result = math::linearInterpolate(
						from, valTo, curve[dt / duration]);
					return result;
				};
				var.markVolatile();
				_entries[&var] = { &var, &ctrl, (float)anip::time() + duration };
			}

			template<typename T>
			static void linear(bindable<T>& var, Control& ctrl, T valTo, float duration)
			{
				static kfloat c({ { 0.0f, 0.0f }, { 1.0f, 1.0f } });
				curve(var, ctrl, valTo, duration, c);
			}

			template<typename T>
			static void easeOutCubic(bindable<T>& var, Control& ctrl, T valTo, float duration)
			{
				static kfloat c = kfloat::fromFunction([](float x) {
					return 1 - powf(1 - x, 3); }, 0.01f);
				curve(var, ctrl, valTo, duration, c);
			}

			template<typename T>
			static void easeInOutCubic(bindable<T>& var, Control& ctrl, T valTo, float duration)
			{
				static kfloat c = kfloat::fromFunction([](float x) { 
					return x < 0.5f ? 4 * powf(x, 3) : 1 - powf(-2 * x + 2, 3) / 2; }, 0.01f);
				curve(var, ctrl, valTo, duration, c);
			}
		};
	}
}